#include "skip_conf.h"
#include "skip_file.h"

#pragma pack (1)
typedef struct _bidx_slot{
	uint64_t key;
	idx_t	next;
	RECPOS	vp;
}bidx_slot;
#pragma pack ()

typedef struct _bidx_head{
	idx_t key_num;
}bidx_head;

typedef struct _slot_idle{
	idx_t	front;
	idx_t	next;
}slot_idle;

typedef struct _bidx_desc{
	int fd;				// key file desc
	struct {
		slot_idle* si_buf;	
		idx_t si_head;
	}si_p;				// idle key slot
	bidx_slot* slot_p;	// key slot	
	idx_t key_num;		// key slot number
}bidx_desc;

#define KEY_START		(sizeof(bidx_head))
#define IDX2POS(idx)	(KEY_START+(idx)*sizeof(bidx_slot))
#define POSATNEXT(pos)	(pos+sizeof(uint64_t))
#define POSATVP(pos)	(POSATNEXT(pos)+sizeof(idx_t))

#define s_hash(sl)		( (uint64_t)(*(sl->buf)) )
#define id_at(dp, id)	( (idx_t)((id)% (uint64_t)((dp)->key_num)) )


static void _skipB_initslot(bidx_desc* bp);
static void _skipB_initidle(bidx_desc* bp);
static idx_t _skipB_pullidle(bidx_desc* bp, idx_t idx);
static void _skipB_pushidle(bidx_desc* bp, idx_t idx);




// the kye_num not's used when file is opend success
bidx_desc* skipB_open(const char* file, long key_num)
{
	bidx_desc* ret;
	bidx_desc bd;
	size_t file_size = s_filesize(file);
	
	if(file == NULL || key_num <=0 )	
		return NULL;

	if(file_size < sizeof(long))
	{
		bidx_head head = {key_num};
		bd.fd = s_create(file);
		bd.key_num = key_num;
		s_write(bd.fd, R_START, (byte*)(&head), sizeof(bidx_head));
		_skipB_initslot(&bd);
	}
	else
	{
		bidx_head head;
		bd.fd = s_open(file);
		
		s_read(bd.fd, R_START, (byte*)(&head), sizeof(bidx_head));
		bd.key_num = head.key_num;
		
		bd.slot_p = (bidx_slot*)malloc(sizeof(bidx_slot)*(bd.key_num));
		s_read(bd.fd, KEY_START, (byte*)(bd.slot_p), sizeof(bidx_slot)*(bd.key_num));
	}

	_skipB_initidle(&bd);
	
	ret = (bidx_desc*)malloc(sizeof(*ret));
	*ret = bd;
	return ret;
}


void skipB_close(bidx_desc* bp)
{
	if(bp==NULL)	
		return;
	free(bp);
}


int skipB_add(bidx_desc* bp, slice* key, RECPOS vp)
{
	uint64_t id = s_hash(key);		// fingerprint
	idx_t idx = id_at(bp, id);
	idx_t b_idx, i;

	if(vp==R_NULL){
		throw_error("the value is R_NULL");
		return SKIP_FALSE;
	}

	if(bp->slot_p[idx].vp==R_NULL && bp->slot_p[idx].next==R_NULL){
		bp->slot_p[idx].key = id;
		bp->slot_p[idx].vp = vp;
		idx = _skipB_pullidle(bp, idx);
		s_write(bp->fd, IDX2POS(idx), (byte*)(&(bp->slot_p[idx])), sizeof(bidx_slot));
		return SKIP_TRUE;
	}

	for( i=idx; i!=R_NULL; i=bp->slot_p[i].next){
		if(bp->slot_p[i].key==id){
			throw_error("the key is redef");
			return SKIP_FALSE;
		}
	}

	b_idx = _skipB_pullidle(bp, idx);
	if(b_idx==R_NULL){
		throw_error("the key is full");
		return SKIP_FALSE;
	}

	// fill the slot
	bp->slot_p[b_idx].key = id;
	bp->slot_p[b_idx].vp = vp;
	if(b_idx != idx){
		bp->slot_p[b_idx].next = bp->slot_p[idx].next;
		bp->slot_p[idx].next = b_idx;
		s_write(bp->fd, POSATNEXT(idx), (byte*)(&(bp->slot_p[idx].next)), sizeof(idx_t));
	}
	s_write(bp->fd, IDX2POS(b_idx), (byte*)(&(bp->slot_p[b_idx])), sizeof(bidx_slot));
	

	return SKIP_TRUE;
}

// find key
RECPOS skipB_find(bidx_desc* bp, slice* key)
{
	uint64_t id = s_hash(key);		// fingerprint
	idx_t idx = id_at(bp, id);

	for(; idx!=R_NULL; idx=bp->slot_p[idx].next){
		if(bp->slot_p[idx].vp==R_NULL)
			continue;

		if(bp->slot_p[idx].key==id)
			return	bp->slot_p[idx].vp;
	}

	return R_NULL;
}

// delete key
RECPOS skipB_del(bidx_desc* bp, slice* key)
{
	uint64_t id = s_hash(key);
	idx_t idx = id_at(bp, id);
	idx_t idx_b = idx;
	for(; idx!=R_NULL;  idx=bp->slot_p[idx].next){
		if(bp->slot_p[idx].vp==R_NULL)
			continue;
		
		if(bp->slot_p[idx].key==id){
			RECPOS ret = bp->slot_p[idx].vp;
			_skipB_pushidle(bp, idx);

			if(bp->slot_p[idx].next==R_NULL){
				bp->slot_p[idx_b].next=R_NULL;
				s_write(bp->fd, POSATNEXT(idx_b), (byte*)(&(bp->slot_p[idx_b].next)), sizeof(idx_t));
			}
			bp->slot_p[idx].vp = R_NULL;
			s_write(bp->fd, POSATVP(IDX2POS(idx)), (byte*)(&(bp->slot_p[idx].vp)), sizeof(RECPOS));
			return ret;
		}

		idx_b = idx;
	}
	
	throw_error("the key not find at del");
	return R_NULL;
}


static void _skipB_initidle(bidx_desc* bp)
{
	idx_t i;
	idx_t tp = R_NULL;
	bp->si_p.si_buf = (slot_idle*)malloc(sizeof(slot_idle)*(bp->key_num));
	bp->si_p.si_head = R_NULL;
	for(i=0; i<bp->key_num; i++)
	{
		if(bp->slot_p[i].vp != R_NULL)
		{
			bp->si_p.si_buf[i].front = R_NULL;
			bp->si_p.si_buf[i].next = R_NULL;
		}
		else
		{
			if(tp==R_NULL){
				bp->si_p.si_buf[i].front = R_NULL;
				bp->si_p.si_head = tp = i;
			}
			else{
				bp->si_p.si_buf[i].front = tp;
				bp->si_p.si_buf[tp].next = i;
			}
			bp->si_p.si_buf[i].next = R_NULL;
		}
	}
}



static void _skipB_initslot(bidx_desc* bp)
{
	idx_t i;
	bidx_slot bs = {0, R_NULL, R_NULL};

	bp->slot_p = (bidx_slot*)malloc(sizeof(bidx_slot)*(bp->key_num));
	bp->si_p.si_buf = (slot_idle*)malloc(sizeof(slot_idle)*(bp->key_num)); 

	for(i=0; i<bp->key_num; i++) {
		s_write(bp->fd, i*sizeof(bidx_slot)+KEY_START, (byte*)(&bs), sizeof(bs));
		bp->slot_p[i] = bs;
	}
}

// pull a idle slot
static idx_t _skipB_pullidle(bidx_desc* bp, idx_t idx)
{
	if(bp->slot_p[idx].vp==R_NULL)
	{
		idx_t n,f;
		f = bp->si_p.si_buf[idx].front;
		n = bp->si_p.si_buf[idx].next;
		if(f!=R_NULL){
			bp->si_p.si_buf[f].next = n;
			bp->si_p.si_head = n;
		}
		if(n!=R_NULL){
			bp->si_p.si_buf[n].front = f;
		}
		return idx;
	}
	else if(bp->si_p.si_head!=R_NULL)
	{
		idx_t ret = bp->si_p.si_head;
		bp->si_p.si_head = bp->si_p.si_buf[ret].next;
		return ret;
	}
	else
		return R_NULL;
}

// push a idle slot
static void _skipB_pushidle(bidx_desc* bp, idx_t idx)
{
	bp->si_p.si_buf[idx].front = R_NULL;
	bp->si_p.si_buf[idx].next = bp->si_p.si_head;
	bp->si_p.si_head = idx;
}

